#include "TestLibusb.h"
#include "string.h"
#include "U3VDebug.h"
#include <unistd.h>

#define LIBUSB_MSG "libusb_msg"

#define BUF_SIZE 512

TestLibusb::TestLibusb()
 : m_ctx(NULL)
 , m_handle(NULL)
 , m_usb_monitor_running(false)
 , m_usb_monitor_thread_id(0)
 , m_hotplug_callback_handle(0)
{
}

TestLibusb::~TestLibusb()
{
    close();

    if (m_ctx)
    {
        libusb_exit(m_ctx);
        m_ctx = NULL;
    }
    printf("========33\n");
}

int TestLibusb::init()
{
    int ret = 0;
    ret = libusb_init(&m_ctx);
    return ret;
}

int TestLibusb::list_devices(void)
{
    int ret     = 0;
    ssize_t cnt = 0;
    libusb_device **devs = NULL;
    

    ret = libusb_init(NULL);
    if (ret < 0)
    {
        return ret;
    }

    cnt = libusb_get_device_list(NULL, &devs);
    if (cnt < 0)
    {
        return (int)cnt;
    }
    
    print_devs(devs, cnt);

    libusb_free_device_list(devs, 1);

    libusb_exit(NULL);

    return 0;
}

int TestLibusb::get_descriptor_with_vid_pid(int vid, int pid)
{
    ssize_t cnt;
    int ret;
    libusb_device **devs = NULL;
    libusb_device *dev = NULL;
    libusb_device *found = NULL;

    ret = libusb_init(NULL);
    if (ret < 0)
    {
        return -1;
    }
    
    cnt = libusb_get_device_list(NULL, &devs);
    if (cnt < 0)
    {
        return -1;
    }

    while(--cnt > 0)
    {
        struct libusb_device_descriptor desc;
        dev = devs[cnt];
        ret = libusb_get_device_descriptor(dev, &desc);
        if (ret < 0)
        {
            return -1;
        }
        if (desc.idVendor == vid && desc.idProduct == pid)
        {
            found = dev;
            break;
        }
    }

    if(NULL == found)
    {
        printf("found no Device\n");
    }

    print_descriptor(0, found);
    
    libusb_free_device_list(devs, 1);

    libusb_exit(NULL);
    return 0;
}

int TestLibusb::open_with_vid_pid(int vid, int pid)
{
    struct libusb_device **devs;
	struct libusb_device *found = NULL;
	struct libusb_device *dev;
	size_t i = 0;
	int ret;

	if (libusb_get_device_list(m_ctx, &devs) < 0)
		return -1;

	while ((dev = devs[i++]) != NULL) 
    {
		struct libusb_device_descriptor desc;
		ret = libusb_get_device_descriptor(dev, &desc);
		if (ret < 0)
			libusb_free_device_list(devs, 1);
		if (desc.idVendor == vid && desc.idProduct == pid) 
        {
			found = dev;
			break;
		}
	}

	if (found) {
		ret = libusb_open(found, &m_handle);
		if (ret < 0)
        {
            printf("open libusb failed, ret[%d]: %s\n", ret, libusb_error_name(ret));
            m_handle = NULL;
        }
        else
        {
            m_usb_status = emOpening;       // 打开状态
        }
        	
	}

    return ret;
}

void TestLibusb::close()
{
    if (NULL != m_handle)
    {
        libusb_close(m_handle);
        m_handle = NULL;
    }
}

int TestLibusb::test()
{
    int ret = 0;
    int nLen = 0;
    ret = open_with_vid_pid(0x2BDF, 0x0001);
    if(ret < 0)
    {
        return -1;
    }

    char send_buf[BUF_SIZE];
    char recv_buf[BUF_SIZE];
    
    // 读写操作
    while(true)
    {
        usleep(200 * 1000);

        // 先读一遍寄存器
        memset(recv_buf, 0, BUF_SIZE);
        nLen = read_debug_info(LOG_IN, recv_buf, BUF_SIZE - 1);
        if(nLen > 0)
        {
            fputs(recv_buf, stdout);
            continue;   // 如果有数据，就再继续读取
        }
        else if(nLen != 0)  // 读取寄存器出错了，程序退出
        {
            printf(C_LRED "read_debug_info error\n" C_NONE);
            break;
        }
        else{
            // 没有数据
        }

        printf(C_LGREEN "\nPCHKVS > " C_NONE);
        fflush(stdout);
        fflush(stdin);
        bool bRealQuit = false;
        // 让用户输入命令
        printf(C_LBLUE "");
        while(fgets(send_buf, 512, stdin) != NULL)
        {
            printf( "" C_NONE);
            if(strlen(send_buf) - 1 == 0)
            {
                printf(C_LGREEN "\nPCHKVS > " C_NONE);
                fflush(stdout);
                continue;
            }
            // 输入的是quit，就退出程序
            if(strncmp(send_buf, "quit", 4) == 0)
            {
                printf("quit...\n");
                bRealQuit = true;
            }
            // 写入的数据小于0，程序退出
            ret = write_debug_info(CMD_OUT, send_buf, strlen(send_buf) - 1);
            if(ret < 0) // 写寄存器出错了
            {
                printf(C_LRED "write_debug_info error\n" C_NONE);
                bRealQuit = true;
            } 
            
            break; 
        }

        if (bRealQuit)
        {
            break;
        }
    }
    // close();
    return 0;
}

void* TestLibusb::usb_monitor_thread(void* arg)
{
    TestLibusb* pUser = reinterpret_cast<TestLibusb*>(arg);
    if (pUser)
    {
        printf("usb monitor thread started.\n");
        int r = 0;
        while (pUser->m_usb_monitor_running) 
        {
            // printf("usb monitor thread running\n");
            r = libusb_handle_events(pUser->m_ctx);
            if (r < 0)
                printf("libusb_handle_events() failed: %s\n", libusb_error_name(r));
        }
        printf("usb monitor thread end.\n");
    }
    return 0;
}
//热插拔的回调监听函数
//不要在回调函数中调用可能阻塞的操作，否则可能造成libusb的其他函数执行失败
int TestLibusb::hotplug_callback(
    struct libusb_context *ctx,
    struct libusb_device *device,
    libusb_hotplug_event event,
    void *user_data) 
{
    //通过设备获取设备地址
    uint8_t deviceAddress = libusb_get_device_address(device);
    //enum libusb_hotplug_event {
    //     LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01,
    //     LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 0x02 }
    if(event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
        printf("设备连接 \n");
    } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
        printf("设备断开 \n");
    }
    return 0;
}


// 注册热插拔事件
int TestLibusb::register_hotplug_callback(int vid, int pid)
{
    int ret = 0;
    //   //设备需要监听的事件，插入和拔出
    libusb_hotplug_event event = static_cast<libusb_hotplug_event> 
            (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);

    //typedef enum {
    //    LIBUSB_HOTPLUG_NO_FLAGS = 0,//只有在发生拔插的时候才会调用注册的回调函数
    //    LIBUSB_HOTPLUG_ENUMERATE = 1<<0,//在初始化前设备已经插入，也会调用注册的回调函数
    //} libusb_hotplug_flag;
    libusb_hotplug_flag flag = static_cast<libusb_hotplug_flag>(0);
    // int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx,
    //                        libusb_hotplug_event events,
    //                        libusb_hotplug_flag flags,
    //                        int vendor_id, int product_id,
    //                        int dev_class,
    //                        libusb_hotplug_callback_fn cb_fn,
    //                        void *user_data,
    //                        libusb_hotplug_callback_handle *callback_handle);

    //这个参数不是很懂
    int device_class = LIBUSB_HOTPLUG_MATCH_ANY;

    m_usb_monitor_running = true;
    ret = pthread_create(&m_usb_monitor_thread_id, NULL, usb_monitor_thread, (void *)this);
    if (ret != 0)
    {
        printf("usb_monitor_thread creation faild, errCode[%d]: %s", errno, strerror(errno));
    }
    pthread_detach(m_usb_monitor_thread_id);

    ret = libusb_hotplug_register_callback(m_ctx, 
        event,
        flag,
        vid,
        pid,
        device_class,
        hotplug_callback, 
        (void *)this, 
        &m_hotplug_callback_handle);
    if (ret != LIBUSB_SUCCESS) {
        printf("resigter hotplug_callback failed\n");
        return -1;
    } else {
        printf("resigter hotplug_callback successfully\n");
    }
    
    return ret;
}

// 取消热插拔事件
void TestLibusb::deregister_hotplug_callback()
{
    printf("deregister_hotplug_callback\n");
    m_usb_monitor_running = false;
    libusb_hotplug_deregister_callback(m_ctx, m_hotplug_callback_handle);
} 

int TestLibusb::read_debug_info(int nMsgType, char* pBuffer, unsigned int nLen)
{
    int ret = libusb_control_transfer(m_handle, 
            LIBUSB_REQUEST_TYPE_VENDOR + LIBUSB_ENDPOINT_IN,
            nMsgType,
            0,
            0,
            reinterpret_cast<unsigned char*>(pBuffer),
            nLen,
            DEBUG_TIME_OUT);
    if(ret < 0)
    {
        printf("libusb_control_transfer errCode[%d]%s\n", ret, libusb_error_name(ret));
    }
    return ret;
}

int TestLibusb::write_debug_info(int nMsgType, char* pBuffer, unsigned int nLen)
{
    int ret = libusb_control_transfer(m_handle, 
            LIBUSB_REQUEST_TYPE_VENDOR + LIBUSB_ENDPOINT_OUT,
            nMsgType,
            0,
            0,
            reinterpret_cast<unsigned char*>(pBuffer),
            nLen,
            DEBUG_TIME_OUT);
    if(ret < 0)
    {
        printf("libusb_control_transfer errCode[%d]%s\n", ret, libusb_error_name(ret));
    }
    return ret;
}

void TestLibusb::print_devs(libusb_device** devs, ssize_t cnt)
{
    int ret = 0;
    libusb_device* dev = NULL;
    libusb_device_handle* handle = NULL;
    struct libusb_device_descriptor desc = { 0 };

    for(ssize_t i = 0; i < cnt; ++i)
    {
        if (NULL == devs[i])
            return;

        dev = devs[i];
        ret = print_descriptor(i, dev);
        if (ret < 0)
            return;
        
    }
}

//打印USB设备的描述符
int TestLibusb::print_descriptor(ssize_t index, libusb_device *dev)
{
    if(dev == NULL)
    {
        return -1;
    }
    struct libusb_device_descriptor desc;
    int ret;

    
    ret = libusb_get_device_descriptor(dev, &desc);
    if (ret < 0) {
        printf("failed to get device descriptor\n");
        return -1;
    }

    printf(C_RED "\n========dev[%zd] pid: 0x%04X, vid: 0x%04X =========\n" C_NONE, 
                    index, desc.idProduct, desc.idVendor);

    print_device(dev, &desc);       
    return 0;
}

// 打印设备描述符
void TestLibusb::print_device(libusb_device *dev, struct libusb_device_descriptor *desc)
{
    int i;
    printf("Device descriptor:\n");
    printf("  bDescriptorType:         %d\n",    desc->bDescriptorType);
    printf("  bcdUSB:                  %#06x\n", desc->bcdUSB);
    printf("  bDeviceClass:            %d\n",    desc->bDeviceClass);
    printf("  bDeviceSubClass:         %d\n",    desc->bDeviceSubClass);
    printf("  bDeviceProtocol:         %d\n",    desc->bDeviceProtocol);
    printf("  bMaxPacketSize0:         %d\n",    desc->bMaxPacketSize0);
    printf("  idVendor:                %#06x\n", desc->idVendor);
    printf("  idProduct:               %#06x\n", desc->idProduct);
    printf("  bNumConfigurations:      %d\n",    desc->bNumConfigurations);

    for (i = 0; i < desc->bNumConfigurations; i++) 
    {
        struct libusb_config_descriptor *config;
        int ret = libusb_get_config_descriptor(dev, i, &config);
        if (LIBUSB_SUCCESS != ret) 
        {
            printf("Couldn't retrieve descriptors\n");
            continue;
        }

        printf(C_GREEN "\n>>>> config[%d] <<<<<\n" C_NONE, i);
        print_configuration(config);
        libusb_free_config_descriptor(config);
    }
}

// 打印配置描述符
void TestLibusb::print_configuration(struct libusb_config_descriptor *config)
{
    int i;
    printf("  Configuration descriptor:\n");
    printf("    wTotalLength:         %d\n",      config->wTotalLength);
    printf("    bNumInterfaces:       %d\n",      config->bNumInterfaces);
    printf("    bConfigurationValue:  %d\n",      config->bConfigurationValue);
    printf("    iConfiguration:       %d\n",      config->iConfiguration);
    printf("    bmAttributes:         0x%02x\n",  config->bmAttributes);
    printf("    MaxPower:             %d mA\n",   config->MaxPower);
    for (i = 0; i < config->bNumInterfaces; i++)
    {
        printf(C_BLUE "  ========== interface[%d] ==========\n" C_NONE, i);
        print_interface(&config->interface[i]);
    }   
}

// 打印接口描述符号
void TestLibusb::print_interface(const struct libusb_interface *interface)
{
    int i;
    for (i = 0; i < interface->num_altsetting; i++)
    {
        printf(C_CYAN "   >>: num_altsetting[%d] \n" C_NONE, i);
        print_altsetting(&interface->altsetting[i]);
    }  
}

// 打印具体的接口配置
void TestLibusb::print_altsetting(const struct libusb_interface_descriptor *interface)
{
    int i;
    printf("    Interface descriptor:\n");
    printf("      bInterfaceNumber:   %d\n", interface->bInterfaceNumber);
    printf("      bAlternateSetting:  %d\n", interface->bAlternateSetting);
    printf("      bNumEndpoints:      %d\n", interface->bNumEndpoints);
    printf("      bInterfaceClass:    %d\n", interface->bInterfaceClass);
    printf("      bInterfaceSubClass: %d\n", interface->bInterfaceSubClass);
    printf("      bInterfaceProtocol: %d\n", interface->bInterfaceProtocol);
    printf("      iInterface:         %d\n", interface->iInterface);
    for (i = 0; i < interface->bNumEndpoints; i++)
    {
        printf(C_BROWN "     >>> endpoint[%d]\n" C_NONE, i);
        print_endpoint(&interface->endpoint[i]);
    }
}

// 打印端点描述符
void TestLibusb::print_endpoint(const struct libusb_endpoint_descriptor *endpoint)
{
    int i, ret;

    printf("      Endpoint descriptor:\n");
    printf("        bEndpointAddress: %02xh\n", endpoint->bEndpointAddress);
    printf("        bmAttributes:     %02xh\n", endpoint->bmAttributes);
    printf("        wMaxPacketSize:   %d\n",    endpoint->wMaxPacketSize);
    printf("        bInterval:        %d\n",    endpoint->bInterval);
    printf("        bRefresh:         %d\n",    endpoint->bRefresh);
    printf("        bSynchAddress:    %d\n",    endpoint->bSynchAddress);
}
